#pragma once

#include "tbb\task_scheduler_init.h"
#include "tbb\parallel_for.h"
#include "tbb\blocked_range.h"
#include "tbb\blocked_range2d.h"
#include "tbb\parallel_reduce.h"
#include "tbb\tick_count.h"

using namespace tbb;

class FuncSolver
{
	TwoDimFunc func;
	double* f_arr;
	int num;
	double a1, b1, h1;

public:
	FuncSolver( double* f_cur, TwoDimFunc f, int n, double a, double b, double h ): f_arr(f_cur), func(f), num(n),
		a1(a), b1(b), h1(h)
	{}

	void operator () ( const blocked_range<int>& r ) const
	{
		int b = r.begin(),
			e = r.end();

		for (int i = b; i != e; i++)
		{
			for (int j = 0; j < num; j++)
			{
				f_arr[i * num + j] = func(a1 + i * h1, a1 + j * h1);
			}
		}
	}
};

class EquSolver
{
	double *cur, *prev, *f;
	int num;
	double h1;

public:
	EquSolver( double* u_cur, double* u_prev, double* f_cur, int n, double h): cur( u_cur ),
		prev( u_prev ), f( f_cur ), num(n), h1(h) 
	{}

	void operator() ( const blocked_range<int>& r ) const
	{
		 int b1 = r.begin();   int e1 = r.end();  
		for (int i = b1; i != e1; i++)
        {
            for (int j = 1; j != num-1; j++)
            {
                cur[i * num + j] = 0.25 * 
                    (prev[(i - 1) * num + j] + prev[(i + 1) * num + j] +
                    prev[i * num + j - 1] + prev[i * num + j + 1] - 
                    h1 * h1 * f[i * num + j]);
            }
		}
	}
};

class ReduceErr
{
	double error;
	double *u_cur, *u_prev;
public:
	explicit ReduceErr( double err, double *cur, double* prev ) : error( err ), u_cur( cur ), u_prev( prev )
	{}

	ReduceErr(const ReduceErr& m, split): u_cur(m.u_cur), u_prev(m.u_prev), error(abs(m.u_cur[0] - m.u_prev[0]))
	{}

	void operator() (const blocked_range<int>& r )
	{
		int b = r.begin(),
			e = r.end();

		double et;
        for (int i = b; i != e; i++)
        {
            et = abs(u_cur[i] - u_prev[i]);
            if (et > error) error = et;
        }
	}
	
	void join( const ReduceErr& re )
	{
		double er = re.error;
		if( er > error )
			error = er;
	}

	double Result()
	{
		return error;
	}
};